#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <fstream>
#include <cassert>
#include<algorithm>
#include<jsoncpp/json/json.h>
#include"../Com/httplib.h"
#include "../Com/Log.hpp"
#include "../Com/Util.hpp"
#include "oj_model.hpp"
// #include "oj_model2.hpp"
#include "oj_view.hpp"

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_LOG;
    using namespace ns_Util;
    using namespace httplib;

    const std::string MachineConf_path = "./conf/servicemachine.conf";

    class Machine
    {
    public:
        std::string _ip;  // 负载均衡机器的ip地址
        int _port;        // 负载均衡机器的端口号
        uint64_t _load;   // 负载均衡机器当前负载
        std::mutex *_mtx; // 负载均衡机器的互斥锁,无法拷贝，只能通过指针传递
    public:
        Machine() : _ip(""), _port(0), _load(0), _mtx(nullptr) {}
        ~Machine() {}

    public:
        // 提升自身负载
        void IncLoad()
        {
            if (_mtx)_mtx->lock();
            _load++;
            if (_mtx) _mtx->unlock();
        }

        // 降低自身负载
        void DecLoad()
        {
            if (_mtx) _mtx->lock();
            _load--;
            if (_mtx) _mtx->unlock();
        }

        //重置负载，防止再次上线保留历史负载
        void ResetLoad()
        {
            if(_mtx) _mtx->lock();
            _load = 0;
            if(_mtx) _mtx->unlock();
        }
        // 获取当前负载，仅为了通用接口
        uint64_t Load()
        {
            uint64_t load = 0;
            if (_mtx)_mtx->lock();
            load = _load;
            if (_mtx) _mtx->unlock();

            return load;
        }
    };

    class LoadBalance
    {
    private:
        std::vector<Machine> _machines;    // 负载均衡机器的集合,利用下标作为负载机器的id标识
        std::vector<int> _onlinemachines;  // 当前可用的负载均衡机器的集合
        std::vector<int> _offlinemachines; // 当前不可用的负载均衡机器的集合
        std::mutex _mtx;                   // 选择负载均衡机器的互斥锁
    public:
        LoadBalance()
        {
            assert(LoadConf(MachineConf_path));
            LOG(INFO) << "加载负载均衡机器配置文件成功" << "\n";
        }
        ~LoadBalance() {}

    public:
        bool LoadConf(const std::string &MachineConf_path)
        {
            // 读取配置文件，将机器信息存入_machines中
            std::ifstream in(MachineConf_path);
            if (!in.is_open())
            {
                LOG(FATAL) << "加载负载均衡机器配置文件失败" << "\n";
                return false;
            }

            // 解析配置文件，将机器信息存入_machines中
            std::string line;
            while (std::getline(in, line))
            {
                std::vector<std::string> tokens;
                FileUtil::SplitString(line, &tokens, ":");

                if (tokens.size() != 2)
                {
                    LOG(ERROR) << "读取切分" << MachineConf_path << "失败" << "\n";
                    continue;
                }

                Machine m;
                m._ip = tokens[0];
                m._port = std::atoi(tokens[1].c_str());
                m._mtx = new std::mutex();

                _onlinemachines.push_back(_machines.size());
                _machines.push_back(m);
            }
            in.close();
            // for(auto m : _machines)
            // {
            //     std::cout << "ip:" << m._ip << " port:" << m._port << std::endl;
            // }
            return true;
        }

        // 将选择好的主机id和机器地址传给上层
        bool SmartChoice(int *id, Machine **m)
        {
            // 1. 使用选择好的主机(更新该主机的负载)
            // 2. 选择失败时，我们需要可能离线该主机
            _mtx.lock();
           int online_num = _onlinemachines.size();
            if (online_num == 0)
            {
                _mtx.unlock();
                LOG(FATAL) << " 所有的后端编译主机已经离线, 请运维的同事尽快查看"
                           << "\n";
                return false;
            }
            // 负载均衡的算法，我们选择第二种
            // 1. 随机数+hash
            // 2. 轮询+hash
           *id = _onlinemachines[0];
            *m = &_machines[_onlinemachines[0]];
            uint64_t min_load = _machines[_onlinemachines[0]].Load();
            for (int i = 1; i < online_num; i++)
            {
                uint64_t curr_load = _machines[_onlinemachines[i]].Load();
                if (min_load > curr_load)
                {
                    min_load = curr_load;
                    *id = _onlinemachines[i];
                    *m = &_machines[_onlinemachines[i]];
                }
            }
            _mtx.unlock();
            return true;
        }

        void OlineMachine()
        {
            // 规定当所有机器离线后，统一上线
            _mtx.lock();
            _onlinemachines.insert(_onlinemachines.end(), _offlinemachines.begin(), _offlinemachines.end());
            _offlinemachines.erase(_offlinemachines.begin(), _offlinemachines.end());
            _mtx.unlock();
        }

        void OfflineMachine(int which)
        {
            // 将某台机器下线，将其从负载均衡机器列表中移除
            _mtx.lock();
            for (auto iter = _onlinemachines.begin(); iter != _onlinemachines.end(); iter++)
            {
                if (*iter == which)
                {
                    _machines[which].ResetLoad();
                    _onlinemachines.erase(iter);
                    _offlinemachines.push_back(which);
                    break;
                }
            }
            _mtx.unlock();
        }

        // 测试使用，展示当前在线和离线列表
        void ShowMachines()
        {
            _mtx.lock();
            std::cout << "当前可用的负载均衡机器列表: ";
            for (int i = 0; i < _onlinemachines.size(); i++)
            {
                std::cout << _onlinemachines[i] << " ";
            }
            std::cout << std::endl;
            std::cout << "当前不可用的负载均衡机器列表: ";
            for (int i = 0; i < _offlinemachines.size(); i++)
            {
                std::cout << _offlinemachines[i] << " ";
            }
            std::cout << std::endl;
            _mtx.unlock();
        }
    };

    class Control
    {
    private:
        Model model_;
        View view_;
        LoadBalance loadbalance_;

    public:
        Control() {}
        ~Control() {}

        void Recovery()
        {
            loadbalance_.OlineMachine();
        }

        bool AllQuestions(std::string *html)
        {
            std::vector<struct Question> questions;
            // std::cout << "1111111" << std::endl;
            if (model_.GetAllQuestions(&questions))
            {
                // for(auto question : questions)
                // {
                //     std::cout << question.number << " " << question.title << std::endl;
                // }
                //使题目列表有序
                sort(questions.begin(), questions.end(), [](const struct Question &a, const struct Question &b) {
                    return a.number < b.number;
                });
                view_.AllExpandHtml(questions, html);
            }
            else
            {
                view_.AllErrorHtml(html);
                return false;
            }
            return true;
        }

        bool Question(const std::string &number, std::string *html)
        {
            struct Question question;
            if (model_.GetQuestion(number, &question))
            {
                view_.OneExpandHtml(question, html);
            }
            else
            {
                view_.OneErrorHtml(number, html);
                return false;
            }
            return true;
        }

        void Judge(const std::string &number, const std::string &in_json, std::string *out_json)
        {
            // 0.分析题目编号，直接拿到对应题目细节信息
            struct Question q;
            if(!model_.GetQuestion(number, &q))
            {
                LOG(ERROR) << "题目" << number << "不存在" << "\n";
            }
            // std::cout << q.tail << std::endl;
            // 1.将用户传入的in_json进行反序列化，得到题目id，用户源代码和input
            Json::Reader reader;
            Json::Value in_value;

            if (!reader.parse(in_json, in_value))
            {
                LOG(ERROR) << "解析用户输入json失败" << "\n";
            }

            std::string code = in_value["code"].asString();
            std::string input = in_value["input"].asString();

            // 2.重新拼接用户代码 + 测试用例代码，形成新代码
            Json::Value compile_value;
            compile_value["code"] = code + "\n" +  q.tail;
            compile_value["input"] = input;
            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;

            Json::FastWriter writer;
            std::string compile_json = writer.write(compile_value);

            // 3.选择负载最低的主机
            while(true)
            {
                int id = 0;
                Machine *m = nullptr;
                if(!loadbalance_.SmartChoice(&id, &m))
                {
                    LOG(FATAL) << "负载均衡选择失败" << "\n";
                    break;
                }

                // 4.向该主机发送编译请求，得到编译结果
                Client cli(m->_ip, m->_port);
                m->IncLoad();
                LOG(INFO) << "选择负载最低的主机： " << id << " : " <<  m->_ip << ":" << m->_port  << " 进行编译，当前负载：" << m->Load() << "\n";
                if(auto res = cli.Post("/compiler_run", compile_json, "application/json;charset=utf-8"))
                {
                    if(res->status == 200)
                    {
                        LOG(INFO) << "编译成功" << "\n";
                        // 5.将结果赋值给result，输出给用户
                        *out_json = res->body;
                        m->DecLoad();
                        // LOG(INFO) << "当前负载："  << m->Load() << "\n";
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    // 5.请求失败，可能是网络问题，或负载均衡选择失败，或编译器故障
                    LOG(ERROR) << "主机：" << id << " : " << m->_ip << ":" << m->_port << " 主机请求失败，可能已经离线" << "\n";
                    loadbalance_.OfflineMachine(id);
                    loadbalance_.ShowMachines();
                }
            }
        }
    };
}